﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.IO;
using stockUploader.Core;
using System.Threading;
using System.ComponentModel;
using System.Windows.Threading;

namespace stockUploader
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private Settings settings = Settings.Load();
        private List<string> extensions = new List<string>(new string[] { ".jpg", ".eps", ".crw" });

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            initData();
        }

        private void initData()
        {
            lbStocks.ItemsSource = settings.Stocks.Select(s => new CheckedStock { Stock = s, IsChecked = true });

            DirectoryInfo di = new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory);
            var files = di.GetFiles()
                .Where(fi => extensions.Contains(fi.Extension))
                .Select(fi => new CheckedFileInfo { FileInfo = fi, IsChecked = true })
                .OrderBy(fi => fi.Name.Remove(fi.Name.Length - fi.Extension.Length))
                .ThenBy(fi => extensions.IndexOf(fi.Extension))
                .GroupBy(fi => fi, new CheckedFileInfoComparer())
                .Select(x => new CheckedFileInfo { FileInfo = x.Key.FileInfo, IsChecked = true, SupportingFiles = x.Where(fi => fi != x.Key) });

            tvFiles.ItemsSource = files;
        }

        private void btnGo_Click(object sender, RoutedEventArgs e)
        {
            Func<CheckedFileInfo, IEnumerable<CheckedFileInfo>> selectMethod = delegate(CheckedFileInfo x)
            {
                var list = x.SupportingFiles.Where(f => f.IsChecked).ToList();
                if(x.IsChecked)
                    list.Insert(0, x);
                return list;
            };

            var filesToUpload = tvFiles.Items.Cast<CheckedFileInfo>().SelectMany(selectMethod).Select(x => x.FileInfo);
            var stocksToUpload = lbStocks.Items.Cast<CheckedStock>().Where(cs => cs.IsChecked).Select(x => x.Stock);

            Worker worker = new Worker
            {
                Files = filesToUpload,
                Stocks = stocksToUpload
            };
            worker.InitOverall += new InitOverallDelegate(worker_InitOverallInvoke);
            worker.InitStock += new InitStockDelegate(worker_InitStockInvoke);
            worker.InitFile += new InitFileDelegate(worker_InitFileInvoke);
            worker.ProgressChanged += new ProgressChangedDelegate(worker_ProgressChangedInvoke);
            worker.FileUploaded += new FileUploadedDelegate(worker_FileUploadedInvoke);
            worker.StockUploaded += new StockUploadedDelegate(worker_StockUploadedInvoke);
            worker.FileUploadError += new FileUploadErrorDelegate(worker_FileUploadErrorInvoke);
            worker.Finished += new FinishedDelegate(worker_FinishedInvoke);

            btnGo.IsEnabled = false;


            Thread thread = new Thread(worker.JustDoIt);
            thread.Start();
        }

        //private List<LogItem> _logItems = null;
        //public List<LogItem> LogItems
        //{
        //    get
        //    {
        //        if(_logItems == null)
        //            _logItems = new List<LogItem>();
        //        if(lbLog.ItemsSource == null)
        //            lbLog.ItemsSource = _logItems;
        //        return _logItems;
        //    }
        //    set
        //    {
        //        _logItems = value;
        //    }
        //}
        private void addToLog(FileInfo fileInfo, Stock stock, Exception ex)
        {
            //LogItems.Add(new LogItem
            //{
            //    DateTime = DateTime.Now,
            //    File = fileInfo,
            //    Stock = stock,
            //    IsSuccess = ex == null,
            //    Message = String.Empty,
            //    Exception = ex
            //});
            if(ex != null)
            {
                tbLog.AppendText(String.Format("{0}: {1} @ {2} : {3}{4}",
                    DateTime.Now,
                    fileInfo.Name,
                    stock.Name,
                    ex != null ? "Error :" + ex.Message : "Uploaded",
                    Environment.NewLine));
            }
        }

        void worker_FinishedInvoke()
        {
            invoke(worker_Finished);
        }
        void worker_Finished()
        {
            btnGo.IsEnabled = true;
        }

        void worker_FileUploadErrorInvoke(FileInfo fileInfo, Stock stock, Exception ex)
        {
            invoke<FileInfo, Stock, Exception>(worker_FileUploadError, fileInfo, stock, ex);
        }
        void worker_FileUploadError(FileInfo fileInfo, Stock stock, Exception ex)
        {
            addToLog(fileInfo, stock, ex);
        }

        void worker_StockUploadedInvoke(Stock stock, long filesSize)
        {
            invoke<Stock, long>(worker_StockUploaded, stock, filesSize);
        }
        void worker_StockUploaded(Stock stock, long filesSize)
        {
            stockUploaded += filesSize;
            totalUploaded += filesSize;
        }

        void worker_FileUploadedInvoke(FileInfo fileInfo, Stock stock)
        {
            invoke<FileInfo, Stock>(worker_FileUploaded, fileInfo, stock);
        }
        void worker_FileUploaded(FileInfo fileInfo, Stock stock)
        {
            addToLog(fileInfo, stock, null);
            worker_ProgressChanged(stock, fileInfo, fileInfo.Length, -1);

            stockUploaded += fileInfo.Length;
            totalUploaded += fileInfo.Length;

            lblImageBytes.Content = lblStockBytes.Content = lblTotalBytes.Content = String.Empty;

        }

        private long stockUploaded = 0;
        private long totalUploaded = 0;

        void worker_ProgressChangedInvoke(Stock stock, FileInfo fileInfo, long uploaded, float speed)
        {
            invoke<Stock, FileInfo, long, float>(worker_ProgressChanged, stock, fileInfo, uploaded, speed);
        }
        void worker_ProgressChanged(Stock stock, FileInfo fileInfo, long uploaded, float speed)
        {
            pbImage.Value = uploaded;
            pbStock.Value = stockUploaded + uploaded;
            pbTotal.Value = totalUploaded + uploaded;

            lblImageBytes.Content = uploaded + " bytes";
            lblStockBytes.Content = fileInfo.Length + " bytes";
            lblTotalBytes.Content = speed > 0 ? speed.ToString("G2") + " KB/s" : String.Empty;
        }

        void worker_InitFileInvoke(FileInfo file, long size)
        {
            invoke<FileInfo, long>(worker_InitFile, file, size);
        }
        void worker_InitFile(FileInfo file, long size)
        {
            pbImage.Maximum = size;
            lblImageName.Content = file.Name;
        }

        void worker_InitStockInvoke(Stock stock, long size)
        {
            invoke<Stock, long>(worker_InitStock, stock, size);
        }
        void worker_InitStock(Stock stock, long size)
        {
            pbStock.Maximum = size;
            lblStockName.Content = stock.Name;
        }

        void worker_InitOverallInvoke(long size)
        {
            invoke<long>(worker_InitOverall, size);
        }
        void worker_InitOverall(long size)
        {
            pbTotal.Maximum = size;
            stockUploaded = 0;
            totalUploaded = 0;
        }

        #region Helper invoke functions
        void invoke<T1, T2, T3, T4>(Action<T1, T2, T3, T4> action, T1 p1, T2 p2, T3 p3, T4 p4)
        {
            this.Dispatcher.Invoke(DispatcherPriority.Normal, action, p1, p2, p3, p4);
        }
        void invoke<T1, T2, T3>(Action<T1, T2, T3> action, T1 p1, T2 p2, T3 p3)
        {
            this.Dispatcher.Invoke(DispatcherPriority.Normal, action, p1, p2, p3);
        }
        void invoke<T1, T2>(Action<T1, T2> action, T1 p1, T2 p2)
        {
            this.Dispatcher.Invoke(DispatcherPriority.Normal, action, p1, p2);
        }
        void invoke<T1>(Action<T1> action, T1 p1)
        {
            this.Dispatcher.Invoke(DispatcherPriority.Normal, action, p1);
        }
        void invoke(Action action)
        {
            this.Dispatcher.Invoke(DispatcherPriority.Normal, action);
        }
        #endregion

    }
}


